package com.jsy.common.util.publicApi.aksk;

//import com.cwn.wizbank.entity.ApiKey;
//import com.cwn.wizbank.services.ApiKeyService;
//import com.cwn.wizbank.utils.SpringContextUtil;
import sun.misc.BASE64Encoder;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentSkipListSet;

/**
 * gates.huang
 * 2025/07/15
 * AK/SK服务端认证工具
 */
public class AkSkAuthenticator {
    
    // 防重放攻击的Nonce缓存（实际应用中应使用Redis等分布式缓存）
    private static final Set<String> usedNonces = new ConcurrentSkipListSet<>();
    private static final long TIMESTAMP_TOLERANCE = 5 * 60 * 1000; // 5分钟

    /**
     * 验证请求签名
     */
    public static boolean verifyRequest(
            String httpMethod, String path, Map<String, String> queryParams, 
            String requestBody, Map<String, String> headers) {
        try {
            // 提取认证信息
            String accessKey = headers.get("x-access-key");
            String signature = headers.get("x-signature");
            String timestampStr = headers.get("x-date");
            String nonce = headers.get("x-nonce");

            // 验证必要参数
            if (accessKey == null || signature == null || timestampStr == null || nonce == null) {
                return false;
            }

            // 验证时间戳
            long timestamp = Long.parseLong(timestampStr);
            long currentTime = System.currentTimeMillis() / 1000;;
            if (Math.abs(currentTime - timestamp) > TIMESTAMP_TOLERANCE) {
                return false;
            }

            // 验证Nonce唯一性（防重放攻击）
            if (!usedNonces.add(nonce)) {
                return false;
            }

            // 获取Secret Key
            String secretKey = null;
//            ApiKeyService apiKeyService = (ApiKeyService) SpringContextUtil.getBean("apiKeyService");
//            ApiKey apiKey = apiKeyService.getApiKeyByAccessKey(accessKey);
//            if (apiKey == null || apiKey.getSecretKeyHash() == null){
//                return false;
//            }else {
//                secretKey = apiKey.getSecretKeyHash();
//            }
            // 计算请求体哈希
            String requestBodyHash = hashRequestBody(requestBody);

            // 构建规范请求
            String canonicalRequest = buildCanonicalRequest(
                httpMethod, path, queryParams, requestBodyHash, timestamp, nonce);

            // 生成预期签名
            String expectedSignature = generateSignature(secretKey, canonicalRequest);
            System.out.println("signature: " + signature);
            System.out.println("expectedSignature: " + expectedSignature);
            // 常量时间比较签名（防时序攻击）
            return MessageDigest.isEqual(
                signature.getBytes(StandardCharsets.UTF_8),
                expectedSignature.getBytes(StandardCharsets.UTF_8));
                
        } catch (Exception e) {
            // 记录异常但不泄露细节
            System.err.println("Authentication error: " + e.getMessage());
            return false;
        }
    }

    /**
     * 构建规范查询字符串
     */
    private static String buildCanonicalQueryString(Map<String, String> queryParams) {
        if (queryParams == null || queryParams.isEmpty()) {
            return "";
        }

        // 将 EntrySet 转换为 List 以便排序
        List<Map.Entry<String, String>> entries = new ArrayList<>(queryParams.entrySet());

        // 按键排序
        Collections.sort(entries, new Comparator<Map.Entry<String, String>>() {
            @Override
            public int compare(Map.Entry<String, String> e1, Map.Entry<String, String> e2) {
                return e1.getKey().compareTo(e2.getKey());
            }
        });

        // 手动拼接字符串
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : entries) {
            if (sb.length() > 0) {
                sb.append("&");
            }
            sb.append(encodeURIComponent(entry.getKey()))
                    .append("=")
                    .append(encodeURIComponent(entry.getValue()));
        }

        return sb.toString();
    }


    /**
     * 构建规范请求
     */
    private static String buildCanonicalRequest(String httpMethod, String path,
                                                Map<String, String> queryParams, String requestBodyHash,
                                                long timestamp, String nonce) {
        String canonicalQueryString = buildCanonicalQueryString(queryParams);

        SortedMap<String, String> canonicalHeaders = new TreeMap<>();
        canonicalHeaders.put("x-date", String.valueOf(timestamp));
        canonicalHeaders.put("x-nonce", nonce);
        canonicalHeaders.put("x-access-key", "dummy-value");

        StringBuilder canonicalHeadersStringBuilder = new StringBuilder();
        for (Map.Entry<String, String> entry : canonicalHeaders.entrySet()) {
            canonicalHeadersStringBuilder.append(entry.getKey().toLowerCase())
                    .append(":")
                    .append(entry.getValue().trim())
                    .append("\n");
        }
        String canonicalHeadersString = canonicalHeadersStringBuilder.toString();
        if (canonicalHeadersString.endsWith("\n")) {
            canonicalHeadersString = canonicalHeadersString.substring(0, canonicalHeadersString.length() - 1);
        }

        StringBuilder signedHeadersBuilder = new StringBuilder();
        for (String key : canonicalHeaders.keySet()) {
            if (signedHeadersBuilder.length() > 0) {
                signedHeadersBuilder.append(";");
            }
            signedHeadersBuilder.append(key.toLowerCase());
        }
        String signedHeaders = signedHeadersBuilder.toString();

        StringBuilder canonicalRequestBuilder = new StringBuilder();
        canonicalRequestBuilder.append(httpMethod).append("\n")
                .append(path).append("\n")
                .append(canonicalQueryString).append("\n")
                .append(canonicalHeadersString).append("\n")
                .append("").append("\n")
                .append(signedHeaders).append("\n")
                .append(requestBodyHash);
        return canonicalRequestBuilder.toString();
    }
    /**
     * 生成签名
     */
    private static String generateSignature(String secretKey, String canonicalRequest) {
        try {
            Mac hmac = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKeySpec = new SecretKeySpec(
                    secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            hmac.init(secretKeySpec);
            byte[] signatureBytes = hmac.doFinal(canonicalRequest.getBytes(StandardCharsets.UTF_8));
            BASE64Encoder base64Encoder = new BASE64Encoder();
            return base64Encoder.encode(signatureBytes);
        } catch (Exception e) {
            throw new RuntimeException("Failed to generate signature", e);
        }
    }

    /**
     * 计算请求体哈希
     */
    private static String hashRequestBody(String requestBody) {
        if (requestBody == null) {
            requestBody = "";
        }
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hashBytes = digest.digest(requestBody.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(hashBytes);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Failed to hash request body", e);
        }
    }

    /**
     * 字节转十六进制
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }

    /**
     * URI组件编码
     */
    private static String encodeURIComponent(String value) {
        if (value == null) {
            return "";
        }
        try {
            return java.net.URLEncoder.encode(value, StandardCharsets.UTF_8.toString())
                .replace("+", "%20")
                .replace("*", "%2A")
                .replace("%7E", "~");
        } catch (Exception e) {
            throw new RuntimeException("Failed to encode URI component", e);
        }
    }
}